use cranelift::prelude::*;

use rustphp_jit::{RustPhpJITBackend, RustPhpJITBuilder};

use cranelift_module::{Linkage, Module};

fn echo(arg: i64) -> i64 {
    print!("{}", arg);

    0_i64
}

/// JIT接口
pub struct RustPhpJitInterface {
    builder_context: FunctionBuilderContext,

    ctx: cranelift_codegen::Context,

    module: Module<RustPhpJITBackend>,
}

impl RustPhpJitInterface {
    /// 新建一个JIT接口
    pub fn new() -> Self {
        let mut builder = RustPhpJITBuilder::new(cranelift_module::default_libcall_names());
        builder.symbol("echo", echo as *const u8);

        let module = Module::new(builder);
        Self {
            builder_context: FunctionBuilderContext::new(),
            ctx: module.make_context(),
            module,
        }
    }

    /// JIT编译过程
    pub fn compile(&mut self, ast: rustphp_parser::ast::AstNode) -> Result<*const u8, String> {
        // 把AST转换成Cranelift IR (intermediate representation)
        self.translate(ast)?;

        // 整个PHP脚本被编译成一个函数，这个函数在IR中取名RustPHP_Main
        // 先declare函数再define函数，这是Cranelift的套路
        let name = "RustPHP_Main";
        let id = self
            .module
            .declare_function(&name, Linkage::Export, &self.ctx.func.signature)
            .map_err(|e| e.to_string())?;

        // define之后，函数的编译过程就完成了
        self.module
            .define_function(id, &mut self.ctx)
            .map_err(|e| e.to_string())?;

        // 编译完成后，context可以清空
        self.module.clear_context(&mut self.ctx);

        // 最后一步，完成外部relocation
        self.module.finalize_definitions();

        // code就是个指向编译后机器码的指针
        let code = self.module.get_finalized_function(id);

        Ok(code)
    }

    /// 把AST转换成IR
    fn translate(&mut self, ast: rustphp_parser::ast::AstNode) -> Result<(), String> {
        let int = self.module.target_config().pointer_type();

        let mut builder = FunctionBuilder::new(&mut self.ctx.func, &mut self.builder_context);

        // ebb == extended basic block
        let entry_ebb = builder.create_ebb();
        builder.append_ebb_params_for_function_params(entry_ebb);
        builder.switch_to_block(entry_ebb);
        builder.seal_block(entry_ebb);

        let mut trans = FunctionTranslator {
            int,
            builder,
            module: &mut self.module,
        };

        trans.translate_expr(ast);

        trans.builder.ins().return_(&[]);
        trans.builder.finalize();

        Ok(())
    }
}

struct FunctionTranslator<'a> {
    int: types::Type,
    builder: FunctionBuilder<'a>,
    module: &'a mut Module<RustPhpJITBackend>,
}

impl<'a> FunctionTranslator<'a> {
    fn translate_expr(&mut self, top_statement_list: rustphp_parser::ast::AstNode) -> Value {
        use rustphp_parser::ast::AstNode::*;
        match top_statement_list {
            TopStatementList(ref list) => {
                for s in list {
                    match s {
                        Echo(v) => {
                            for n in v {
                                match n {
                                    Int(m) => {
                                        let args = vec![*m];
                                        self.translate_call("echo".to_string(), args);
                                    }
                                    String(s) => {
                                        println!("===========debug info===========");
                                        println!("TODO:echo 字符串 - {}", s);
                                        println!();
                                    }
                                    _ => panic!(),
                                }
                            }
                        }
                        _ => {
                            // do nothing
                        }
                    }
                }
            }

            _ => panic!(),
        }

        // 目前对Expr简单返回0
        self.builder.ins().iconst(self.int, 0)
    }

    fn translate_call(&mut self, name: String, args: Vec<i64>) -> Value {
        let mut sig = self.module.make_signature();

        for _arg in &args {
            sig.params.push(AbiParam::new(self.int));
        }

        sig.returns.push(AbiParam::new(self.int));

        let callee = self
            .module
            .declare_function(&name, Linkage::Import, &sig)
            .expect("声明函数出错");
        let local_callee = self
            .module
            .declare_func_in_func(callee, &mut self.builder.func);

        let mut arg_values = Vec::new();
        for arg in args {
            arg_values.push(self.builder.ins().iconst(self.int, arg));
        }
        let call = self.builder.ins().call(local_callee, &arg_values);

        self.builder.inst_results(call)[0]
    }
}
